/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package org.opensource.jdom.taggen.util;

import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.jdom.Attribute;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import org.opensource.jdom.taggen.Tag;

/**
 * Utility for some commons task for attributes of a tag.
 *
 * @author sergio.valdez
 */
public class AttributeUtil {

    public static final String GET          = "get";
    public static final String IS           = "is";
    public static final String CSS_PREFIX   = "css";
    public static final String LOW_GUIDE    = "_";
    public static final String MEDIUM_GUIDE = "-";

    /**
     * <p>
     * This method will be create all attributes for a given tag i.e. if you
     * have a Tag(Any instance for this Object) called Div and this have the
     * fallow fields: 
     * <ul>
     *  <li>id</li>
     *  <li>cssClass</li>
     *  <li>cssStyle</li>
     *  <li>title</li>
     * </ul> 
     * and his respective
     * getters and setters the result will be:
     * </p>
     * <ul>
     *  <li>id="GIVEN_VALUE"</li>
     *  <li>class="GIVEN_VALUE"</li>
     *  <li>style="GIVEN_VALUE"</li>
     *  <li>title="GIVEN_VALUE"</li>
     * </ul>
     * 
     */
    public static void createAttributes(Tag tag,
            String excludedMethods) throws IllegalAccessException,
            IllegalArgumentException, InvocationTargetException
    {
        try {
            SettingsUtil.createDefaultProps(tag.getClass().getPackage()
                    .toString());
        } catch (IOException ex) {
            Logger.getLogger(AttributeUtil.class.getName())
                    .log(Level.SEVERE, null, ex);
        }
        
        Method[] methods = tag.getClass().getMethods();
        Attribute attr = null;

        for (Method method : methods) {//Iterate all methods
            if (isValidMethod(method, tag)) {//Verify if it is a valid method
                //Get the value of this method
                Object value = getMethodValue(tag, method);
                if (value != null) {//Not null methods

                    //If the getted value is boolean the value for this
                    //attribute will be the name of the attribute 
                    //e.g. if you have a property called readonly and it is a 
                    //boolean datatype the result attribute will be 
                    //readonly="readonly"
                    if (isBoolean(method)){
                        if ((Boolean)value) {
                            attr = new Attribute(
                                    getStandarAttributeName(method, tag),
                                    getStandarAttributeName(method, tag)
                                );
                        }
                    } else {
                        attr = new Attribute(
                                getStandarAttributeName(method, tag),
                                (String)value
                            );
                    }//else
                }//if null
                if (attr != null) tag.setAttribute(attr);

                attr = null;
                value = null;
            }//if contains
        }//for methods

    }

    /**
     * Standarize the names for attributes in XML/HTML follow are the rules:
     *
     * -Attributes will be changed to lowor case.
     * -Low Guide char(_) will be changed to Medium Guide Character(-).
     * -The first prefix "get" or "is" will be removed also "css" prefix.
     *
     * @param method Method from a class.
     */
    private static String getStandarAttributeName(Method method, Tag tag) {
        Settings settings = null;
        try {
            settings = SettingsUtil.createSettings(tag.getClass()
                    .getPackage().toString());
        } catch (IOException ex) {
            Logger.getLogger(AttributeUtil.class.getName()).log(Level.SEVERE, null, ex);
        } catch (ClassNotFoundException ex) {
            Logger.getLogger(AttributeUtil.class.getName()).log(Level.SEVERE, null, ex);
        }

        String replaceLWFrom  = LOW_GUIDE;
        String replaceLWTo    = MEDIUM_GUIDE;
        String replacePreFrom = CSS_PREFIX;
        String replacePreTo   = "";
        boolean lowercased    = true;

        if (settings!=null) {
            replaceLWFrom   = settings.getAttReplaceLowguideFrom();
            replaceLWTo     = settings.getAttReplaceLowguideTo();
            replacePreFrom  = settings.getAttReplacePrefixFrom();
            replacePreTo    = settings.getAttReplacePrefixTo();
            lowercased      = settings.isAttLowercased();
        }

        String result = method.getName().replaceFirst(GET, "").replaceFirst(IS, "")
                .replaceFirst(replacePreFrom, replacePreTo)
                .replaceAll(replaceLWFrom, replaceLWTo);

        if (lowercased) {
            result = result.toLowerCase();
        }

        return  result;
    }

    /**
     * Get the value cointained in certain method.
     *
     * @param method Method from a class.
     * @param obj Object from where the methos is taked of.
     * @return Value of the given method in gived Object.
     */
    private static Object getMethodValue(Object obj, Method method) throws
            IllegalAccessException, IllegalArgumentException,
            InvocationTargetException
    {
        return method.invoke(obj, (Object[])null);
    }

    /**
     * Tell us if the methos gived is a valid methos to created a tag Property.
     * if the method gived is a methos form the object gived and his name has
     * a prefix like "get" or "is" it is a valid methos also if the methos gived
     * are field in fallow Classes CoreAttributes, EventAttributes,
     * CommonAttributes, I18nAttributes, I18nEventAttributes,
     * CoreEventAttributes
     *
     * @param method Method from a class.
     * @param obj Object from where the methos is taked of.
     * @return true if the gived method is valid.
     *
     *
                    || method.getDeclaringClass().equals(CoreAttributes.class)
                    || method.getDeclaringClass().equals(EventAttributes.class)
                    || method.getDeclaringClass().equals(CommonAttributes.class)
                    || method.getDeclaringClass().equals(I18nAttributes.class)
                    || method.getDeclaringClass().equals(
                                                    I18nEventAttributes.class)
                    || method.getDeclaringClass().equals(
                                                    CoreEventAttributes.class)
     */
    private static boolean isValidMethod(Method method, Object obj){
        return (
                (
                    method.getDeclaringClass().equals(obj.getClass())
                    || isMethodInValidClass(method, (Tag)obj)
                ) && (
                    method.getName().startsWith(GET) || method.getName()
                    .startsWith(IS)
                )
               );
    }

    /**
     * Tell us if the return value of certain mathod is boolean type.
     *
     * @param method Method from a class.
     * @return true if the return type of gived method is boolean type.
     */
    private static boolean isBoolean(Method method) {
        return method.getReturnType().equals(boolean.class);
    }

    private static boolean isMethodInValidClass(Method method, Tag mainTag){
        boolean result = false;

        try {
            SettingsUtil.createDefaultProps(mainTag.getClass()
                    .getPackage().toString());
            Settings settings = SettingsUtil.createSettings(mainTag.getClass()
                    .getPackage().toString());
                    
            for (Class clazz : settings.getAttFromClasess()) {
                if (method.getDeclaringClass().equals(clazz)) {
                    result = true;
                    break;
                }
            }

        } catch (IOException ex) {
            Logger.getLogger(AttributeUtil.class.getName())
                    .log(Level.SEVERE, null, ex);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }

        return result;
    }
}
